#include "Parser.h"

#include "Globals.h"
#include <sys/mman.h>
#include "WindowReader.h"
#include <string.h>

#ifdef	DEBUG
 #include <stdio.h>
using namespace std;
#endif	/* DEBUG */


Parser::Parser(int _fd, size_t _fromPos, off_t _fileSize) :
		fd(_fd),
		fromPos(_fromPos),
		fileSize(_fileSize)
///		titleLines()
{
}

Parser::~Parser()
{
///	for (vector<char *>::iterator it = titleLines.begin(); it != titleLines.end(); it++)
///		delete *it;
}

/*
 * Parses the chunk of the given size and returns a vector of all Matches that were found in that chunk.
 */
char *Parser::parseChunk(uint chunkSize, vector<Name *> &names, size_t &badCharsSLN, size_t &badMM)
///vector<Match*> *Parser::parseChunk(uint chunkSize)
{
	bool firstLineRead = false;
	bool readingTitle = false;
	int titleLineSize = 0;
	WindowReader window(0);
	uint curWindowSize = 0;

	// Using 2 vectors of SubMatch so we interchange between the two as current and previous
	vector<SubMatch> curSubMatchesA;
	vector<SubMatch> curSubMatchesB;
	vector<SubMatch> *curSubMatches = &curSubMatchesA;
	vector<SubMatch> *curSubMatchesOther = &curSubMatchesB;

	// Map the given file chunk into a buffer
/*	off_t readSize;
	if (fileSize < fromPos + chunkSize)
		readSize = fileSize - fromPos;
	else
		readSize = fromPos + chunkSize;
*/
	// Map the file to the memory
	char *data = (char *)mmap(NULL, chunkSize, PROT_READ, MAP_PRIVATE, fd, fromPos);
	if (data == MAP_FAILED)
	{
		perror("Error doing mmap\n");
		return NULL;
	}
	char *dataEnd = data + chunkSize;
	char *titleFirstChar = data;
	char *p = data;

	char *beginStr = NULL;

	char *curAngle = data;

	int trashChars = 0; // Counts the number of non-good chars so far

	badMM = 0;

	Name *curName = new Name(NULL, 0);
	names.push_back(curName);


	// Read the data into a sequence
	for (; p != dataEnd; p++)
	{
		// If we are sure we are reading a seq name
		if (readingTitle)
		{
			trashChars++;
			if (dataEnd - p < minMatchLength)
				badMM++;
			if (*p == '\n')
			{
				if (p - titleFirstChar > 0)
				{
					char *titleLine = new char[p - titleFirstChar + 1];
					memcpy(titleLine, titleFirstChar, sizeof(char)*(p - titleFirstChar));
					titleLine[p - titleFirstChar] = '\0';
///					titleLines.push_back(titleLine);
					curName->name = titleLine;
					curName->fromPos = sizeof(char) * (curAngle - data);
///					names.push_back(new Name(titleLine, sizeof(char) * (curAngle - data)));
#ifdef	DEBUG
					cout << "Read titleLine 1: " << titleLine << endl;
#endif	/* DEBUG */
				}
				firstLineRead = true;
				readingTitle = false;
				curWindowSize = 0;
			}
		}
		else
		{

			// Maybe we are reading a title-line or a seq-line
			// Read the first line into both the titleLine array and the sequence
			if ((!firstLineRead) && (*p == '\n'))
			{
				if (p - titleFirstChar > 0)
				{
					beginStr = new char[p - titleFirstChar + 1];
					memcpy(beginStr, titleFirstChar, sizeof(char)*(p - titleFirstChar));
					beginStr[p - titleFirstChar] = '\0';
///					titleLines.push_back(titleLine);
#ifdef	DEBUG
					cout << "Read titleLine 2 (beginStr): " << beginStr << endl;
#endif	/* DEBUG */
				}
				firstLineRead = true;
			}

			bool validChar = false;

			// Parse it as a sequence
			switch (*p)
			{
			case '\n':
				trashChars++;
				if (dataEnd - p < minMatchLength)
					badMM++;
				// Do nothing...
				break;

			case '>':
				// We know we are in a Title-line, note that and wait until we reach '\n'
				// Close any matches that may have not been closed
				if (curSubMatches->size() > 0)
				{
					vector<SubMatch>::const_iterator subsIt = curSubMatches->begin();
					// No matches for current sub-string, need to "abort" all current sub-matches
					while (subsIt != curSubMatches->end())
					{
						///results.push_back(new Match(subsIt->refPos + 1, subsIt->inPos + 2 - windowSize, windowSize + subsIt->length - 1, subsIt->trashChars));
						if (windowSize + subsIt->length >= minMatchLength)
							curName->matches->push_back(new Match(subsIt->refPos + 1, subsIt->inPos + 2 - windowSize, windowSize + subsIt->length - 1, subsIt->trashChars));

						if (subsIt->refPos + 1 == 1619)
							cout << "Here1" << endl;
						subsIt++;
					}
					curSubMatches->clear();
				}


				if (p != data)
				{
					curName = new Name(NULL, (p - data));
					names.push_back(curName);
				}


				// Reset the rest of the stuff
				readingTitle = true;
				titleFirstChar = p + 1;
				curWindowSize = 0;
				trashChars = 1;
				if (dataEnd - p < minMatchLength)
					badMM++;
				curAngle = p;
				break;

			case 'A':
				validChar = true;
				window.push(CHAR_A);
				curWindowSize++;
				break;

			case 'C':
				validChar = true;
				window.push(CHAR_C);
				curWindowSize++;
				break;

			case 'G':
				validChar = true;
				window.push(CHAR_G);
				curWindowSize++;
				break;

			case 'T':
				validChar = true;
				window.push(CHAR_T);
				curWindowSize++;
				break;


			default:
				curWindowSize = 0;
				trashChars++;
				if (dataEnd - p < minMatchLength)
					badMM++;
				break;
			}


			if ((validChar) && (curWindowSize >= windowSize))
			{
#ifdef	DEBUG
				cout << "Processing window: ";
				window.printStr();
				cout << endl;
#endif	/* DEBUG */
				const vector<uint> *windowMatches = refSeq->getSubsHash()->getSubstringPositions(window.getContents());
				if (windowMatches != NULL)
				{
#ifdef	DEBUG2
					cout << endl;
					cout << "\tThe window matches positions: ";
					for (vector<uint>::const_iterator it = windowMatches->begin(); it != windowMatches->end(); it++)
						cout << *it << ", ";
					cout << endl;
					cout << "\t\tCurrent sub-matches start positions are (in the format \" [inPos(length)->refPos] \"): ";
					for (vector<SubMatch>::const_iterator it = curSubMatches->begin(); it != curSubMatches->end(); it++)
						cout << "[" << it->inPos << " (" << it->length << ")->" << it->refPos << "], ";
					cout << endl;
#endif	/* DEBUG2 */
					vector<uint>::const_iterator winIt = windowMatches->begin();
					vector<SubMatch>::const_iterator subsIt = curSubMatches->begin();
					while ((winIt != windowMatches->end()) && (subsIt != curSubMatches->end()))
					{
						if (*winIt == (subsIt->refPos + subsIt->length + 1))
						{
							// Found a continuation of the previous sub-match
							SubMatch updateSub(subsIt->inPos, subsIt->refPos, subsIt->length + 1, subsIt->trashChars);
							curSubMatchesOther->push_back(updateSub);
							winIt++;
							subsIt++;
#ifdef	DEBUG2
							cout << "\t\t\tFound a continuation SubMatch - new SubMatch ( \" [inPos(length)->refPos] \"): ";
							cout << "[" << updateSub.inPos << " (" << updateSub.length << ")->" << updateSub.refPos << "], ";
							cout << endl;
#endif	/* DEBUG2 */
						}
						else if (*winIt <= (subsIt->refPos + subsIt->length))
						{
							// A new sub-match, add it to the list
							SubMatch newSub(p - curAngle, *winIt, 0, trashChars);
							curSubMatchesOther->push_back(newSub);
							winIt++;
#ifdef	DEBUG2
							cout << "\t\t\tFound a new SubMatch ( \" [inPos(length)->refPos] \"): ";
							cout << "[" << newSub.inPos << " (" << newSub.length << ")->" << newSub.refPos << "], ";
							cout << endl;
#endif	/* DEBUG2 */
						}
						else
						{
							// The old sub-match is over, add the match to the results list
							///results.push_back(new Match(subsIt->refPos + 1, subsIt->inPos + 2 - windowSize, windowSize + subsIt->length - 1, subsIt->trashChars));
							if (windowSize + subsIt->length >= minMatchLength)
							{
								curName->matches->push_back(new Match(subsIt->refPos + 1, subsIt->inPos + 2 - windowSize, windowSize + subsIt->length - 1, subsIt->trashChars));
								if (subsIt->refPos + 1 == 1619)
									cout << "Here2" << endl;
#ifdef	DEBUG2
								cout << "\t\t\tClosing a finished SubMatch ( \" [inPos(length)->refPos] \"): ";
								cout << "[" << subsIt->inPos << " (" << subsIt->length << ")->" << subsIt->refPos << "], ";
								cout << endl;
#endif	/* DEBUG2 */
							}
							subsIt++;
						}
					}
					// Just take the residue of the two lists, and update the correct vectors
					while (winIt != windowMatches->end())
					{
						SubMatch newSub(p - curAngle, *winIt, 0, trashChars);
						curSubMatchesOther->push_back(newSub);
						winIt++;
#ifdef	DEBUG2
						cout << "\t\t\tFound a new SubMatch2 ( \" [inPos(length)->refPos] \") (trash=" << trashChars << "): ";
						cout << "[" << newSub.inPos << " (" << newSub.length << ")->" << newSub.refPos << "] ";
						cout << endl;
#endif	/* DEBUG2 */
					}
					while (subsIt != curSubMatches->end())
					{
						///results.push_back(new Match(subsIt->refPos + 1, subsIt->inPos + 2 - windowSize, windowSize + subsIt->length - 1, subsIt->trashChars));
						if (windowSize + subsIt->length >= minMatchLength)
						{
							curName->matches->push_back(new Match(subsIt->refPos + 1, subsIt->inPos + 2 - windowSize, windowSize + subsIt->length - 1, subsIt->trashChars));
							if (subsIt->refPos + 1 == 1619)
								cout << "Here3" << endl;
#ifdef	DEBUG2
							cout << "\t\t\tClosing a finished SubMatch2 ( \" [inPos(length)->refPos] \"): ";
							cout << "[" << subsIt->inPos << " (" << subsIt->length << ")->" << subsIt->refPos << "], ";
							cout << endl;
#endif	/* DEBUG2 */
						}
						subsIt++;
					}
					// Clear the old sub match vector and replaces the current and the other vectors...
					curSubMatches->clear();
					vector<SubMatch> *temp = curSubMatches;
					curSubMatches = curSubMatchesOther;
					curSubMatchesOther = temp;
				}
				else if (curSubMatches->size() > 0)
				{
					vector<SubMatch>::const_iterator subsIt = curSubMatches->begin();
					// No matches for current sub-string, need to "abort" all current sub-matches
					while (subsIt != curSubMatches->end())
					{
						///results.push_back(new Match(subsIt->refPos + 1, subsIt->inPos + 2 - windowSize, windowSize + subsIt->length - 1, subsIt->trashChars));
						if (windowSize + subsIt->length >= minMatchLength)
						{
							curName->matches->push_back(new Match(subsIt->refPos + 1, subsIt->inPos + 2 - windowSize, windowSize + subsIt->length - 1, subsIt->trashChars));
#ifdef	DEBUG
							cout << "\t\t\tClosing a finished SubMatch3 ( \" [inPos(length)->refPos] \"): ";
							cout << "[" << subsIt->inPos << " (" << subsIt->length << ")->" << subsIt->refPos << "], ";
							cout << endl;
#endif	/* DEBUG */
						}
						subsIt++;
					}
					// Clear the old sub match vector and replaces the current and the other vectors...
					curSubMatches->clear();
					vector<SubMatch> *temp = curSubMatches;
					curSubMatches = curSubMatchesOther;
					curSubMatchesOther = temp;
				}
			}

		}

	}

	// Close any matches that may have not been closed
	if (curSubMatches->size() > 0)
	{
		vector<SubMatch>::const_iterator subsIt = curSubMatches->begin();
		// No matches for current sub-string, need to "abort" all current sub-matches
		while (subsIt != curSubMatches->end())
		{
			///results.push_back(new Match(subsIt->refPos + 1, subsIt->inPos + 2 - windowSize, windowSize + subsIt->length - 1, subsIt->trashChars));
			if (windowSize + subsIt->length >= minMatchLength)
			{
				curName->matches->push_back(new Match(subsIt->refPos + 1, subsIt->inPos + 2 - windowSize, windowSize + subsIt->length - 1, subsIt->trashChars));
							if (subsIt->refPos + 1 == 1619)
								cout << "Here5" << endl;
#ifdef	DEBUG
				cout << "\t\t\tClosing a finished SubMatch4 ( \" [inPos(length)->refPos] \"): ";
				cout << "[" << subsIt->inPos << " (" << subsIt->length << ")->" << subsIt->refPos << "], ";
				cout << endl;
#endif	/* DEBUG */
			}
			subsIt++;
		}
	}


	// If we were reading a title or reading just one line in the chunk
	if ((readingTitle) || (!firstLineRead))
	{
		if (*p == '\n')
		{
			if (p - titleFirstChar > 0)
			{
				char *titleLine = new char[p - titleFirstChar + 1];
				memcpy(titleLine, data, sizeof(char)*(p - titleFirstChar));
				titleLine[p - titleFirstChar] = '\0';
				if (readingTitle)
				{
					curName = new Name(titleLine, sizeof(char) * (curAngle - data));
//					names.push_back(curName);
///					names.push_back(new Name(titleLine, sizeof(char) * (curAngle - data)));
				}
				else
					beginStr = titleLine;
			}
		}
	} // for p


	// If the whole chunk was a single line, still need to copy it
	// unmap the data
	munmap(data, chunkSize);

	badCharsSLN = trashChars;



#ifdef	DEBUG
	cout << "*************************** Done parsing chunk ****************************" << endl;
	cout << "Results are: " << endl;
	for (vector<Name*>::const_iterator namesIt = names.begin(); namesIt != names.end(); namesIt++)
	{
		if ((*namesIt)->name == NULL)
			cout << "\tName: NULL, pos=" << (*namesIt)->fromPos << endl;
		else
			cout << "\tName: " << (*namesIt)->name << ", pos=" << (*namesIt)->fromPos << endl;
		for (vector<Match*>::const_iterator it = (*namesIt)->matches->begin(); it != (*namesIt)->matches->end(); it++)
		{
			(*it)->print();
		}
		cout << endl;
	}

	if (beginStr == NULL)
		cout << "BeginSTr is NULL" << endl;
	else
		cout << "BeginSTr is " << beginStr << endl;
#endif	/* DEBUG */

	return beginStr;
}

